package com.qf.push.web.server;

import com.alibaba.fastjson.JSONObject;


import com.qf.base.utils.SpringContextUtil;
import com.qf.push.dao.MessageDao;
import com.qf.push.model.Message;
import com.qf.push.model.MessageExample;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@ServerEndpoint("/webSocket/{uid}")
@Component
@Slf4j
public class WebSocketServer {
    private MessageDao messageDao;
    //统计目前连接网页端的数量
    //前端连接后端，每一个连接都是一个线程
    //每一个连接都让onlineNum自增
    //每断开一个连接让onlineNum自减
    //automicInteger
    //基于int基本数据类型，在多线程并发情况下，封装的原子类
    private static AtomicInteger onlineNum=new AtomicInteger();
    public static void addOnlineCount(){
        //安全自增
        onlineNum.incrementAndGet();
    }
    public static void subOnlineCount(){
        //安全自减
        onlineNum.decrementAndGet();
    }
    //需要对用户发消息
    //此session非二阶段的session
    //它是websocket协议下的会话
    public void sendMessage(Session session,String message) throws IOException {
        if (session!=null){
            synchronized (session){
                //为了防止多线程下对同一个事情发送消息，所以逐一发送消息
                //锁住会话，逐一发送给这个会话消息
                session.getBasicRemote().sendText(message);
            }

        }
    }

    /**
     * 根据用户id发送消息的方法
     */
    public boolean sendInfo(String uid,String message){
        Session session=sessionPool.get(uid);
        //如果为null证明是离线状态返回false
        if (session==null){
            return false;
        }
        //如果不为null就是在线状态，发送消息，并且返回true
        try {
            sendMessage(session,message);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }


    //保存已经和用户连接的所有的会话
    //根据用户的id保存这些会话
    //使用map数据结构来维护
    //因为他是一个多线程的场景
    //所以采用ConcurrentHashMap
    private static ConcurrentHashMap<String,Session> sessionPool=new ConcurrentHashMap<>();

    /**
     * 用户和服务器连接成功，此方法就被运行
     * @param session   session就是浏览器的会话
     * @param uid   用户穿过来的id
     */

    @OnOpen
    public void onOpen(Session session, @PathParam(value = "uid")String uid){
        //连接成功  就把uid和session保存到map当中
        sessionPool.put(uid,session);
        //连接树增加
        addOnlineCount();
        //根据class来获取注入的springbean对象
        if (messageDao==null){
            messageDao= SpringContextUtil.getBean(MessageDao.class);
        }
        //查询数据库，寻找uid对应的未推送的消息，一次性推送至前端
        MessageExample example=new MessageExample();
        MessageExample.Criteria criteria = example.createCriteria();
        criteria.andUidEqualTo(Long.parseLong(uid));
        criteria.andStateEqualTo(0);
        List<Message> messages = messageDao.selectByExample(example);
        List<JSONObject> msgs=new ArrayList<>();
        for (int i=0;i<messages.size();i++){
            Message message=messages.get(i);
            JSONObject msg=new JSONObject();
            msg.put("uid", message.getUid()) ;
            msg.put("type", message.getType());
            msg.put("content", message.getContent());
            msgs.add(msg);
        }
        try {
            Map<String,Object> map=new HashMap<>();
            map.put("init", 1);
            map.put("messages", msgs);
            sendMessage(session,JSONObject.toJSONString(map));
            Message msg=new Message();
            msg.setState(1);
            messageDao.updateByExampleSelective(msg,example);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 当前端与服务器断开连接，就被运行的方法
     * @param uid   用户的id
     */
    @OnClose
    public void onClose(Session session,@PathParam(value = "uid")String uid){
        //删除对应会话
        sessionPool.remove(uid);
        //在线人数减少
        subOnlineCount();

    }

    /**
     * 当前端给我们发送消息，这个方法就被运行
     * @param message 前端发给我们的消息
     */
    @OnMessage
    public void onMessage(String message){
        log.info(message);
    }
    @OnError
    public void onError(Session session, Throwable throwable ){ throwable.printStackTrace(); }
}
